package cn.ljy.authority.service.impl;

import cn.hutool.core.map.BiMap;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.ljy.authority.dao.mapper.SysDepartmentMapper;
import cn.ljy.authority.dao.mapper.SysRoleMapper;
import cn.ljy.authority.dao.mapper.SysUserMapper;
import cn.ljy.authority.dao.mapper.SysUserRoleMapper;
import cn.ljy.authority.model.SysDepartment;
import cn.ljy.authority.model.SysRole;
import cn.ljy.authority.model.SysUser;
import cn.ljy.authority.model.SysUserRole;
import cn.ljy.authority.model.query.SysUserQuery;
import cn.ljy.authority.service.SysUserExcelService;
import cn.ljy.authority.service.SysUserService;
import cn.ljy.common.exception.model.MyExcelException;
import cn.ljy.common.exception.model.MyServiceException;
import cn.ljy.common.exception.model.base.MyRollBackException;
import cn.ljy.common.model.result.ExcelImportResult;
import cn.ljy.common.utils.password.SHA;
import cn.ljy.common.utils.table.ExcelExceptionMessage;
import cn.ljy.common.utils.table.MyHutoolCustomExcelUtil;
import cn.ljy.common.utils.table.MyTableUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotNull;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 要在一般业务类只上构建的
 */
@Service
@Validated
@Slf4j
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor= MyRollBackException.class)
public class ISysUserExcelService implements SysUserExcelService {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysRoleMapper sysRoleMapper;
    @Autowired
    private SysDepartmentMapper sysDepartmentMapper;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    private String sheetName="账户列表";//要查找的账户子表
    //性别
    private final static HashMap<Integer, String> SEX_MAP = new HashMap<Integer, String>(){
        {
            put(0, "保密");
            put(1, "男");
            put(2, "女");
        }
    };
    //账户状态
    private final static HashMap<Integer, String> STATUS_MAP = new HashMap<Integer, String>(){
        {
            put(0, "不可用");
            put(1, "可用");
            put(2, "锁定");
        }
    };
    //定义表格的行对象，用于处理表格中的bean
    @Data
    private class SysUserRow{
        private Long id;
        private String username;//账户名
        private String nickName;//昵称
        private String sexString;//性别
        private String statusString;//账户状态
        private String roleNames;//角色集合
        private String departmentName;//部门名称
        private String description;//账户描述
        private Date createTime;//创建时间
        private Date updateTime;//最后编辑时间
    }
    //表格标题栏和实体的映射,必须为有序map集合
    private final static HashMap SYSUSERBEAN_MAP = new LinkedHashMap<String, String>(){
        {
            put("id","用户编号");
            put("username","账户名");
            put("nickName","昵称");
            put("sexString","性别");
            put("statusString","账户状态");
            put("roleNames","角色");
            put("departmentName","部门名称");
            put("description","账户描述");
            put("createTime","创建时间");
            put("updateTime","最后编辑时间");
        }
    };

    /**
     * 将user对象转换为要显示的表格行记录
     * @param user 应该是从数据库里面读取出来的对象。如果是新建对象，应该包含必须的属性（sex、status）
     * @param isTemplate 是否是模板。
     * @return
     */
    private SysUserRow convertSysUserToExcelRow(SysUser user,boolean isTemplate){
        SysUserRow row=null;
        if(user!=null){
            //定义行记录对象
            row=new SysUserRow();
            //属性拷贝,会将相同属性的值拷贝给新对象
            BeanUtils.copyProperties(user, row);
            //设置性别的标签
            row.setSexString(SEX_MAP.get(user.getSex()));
            //设置状态的标签
            row.setStatusString(STATUS_MAP.get(user.getStatus()));
            //设置角色列表
            //设置角色列表下拉框。因为角色可以有多个，暂时不进行设置
            //writer.addSelect(5,1,nameZhs.toArray(new String[nameZhs.size()]));
            if(isTemplate){//如果是示例
                List<String> nameZhs=sysRoleMapper.getAllNameZhs();//查找数据库所有中文角色名
                if(nameZhs.size()==1){//如果数据库只有一个角色
                    row.setRoleNames(nameZhs.get(0));
                }else if(nameZhs.size()>1){//如果数据库有多个角色，则显示多个角色的示例，例如：超级管理员|普通管理员
                    row.setRoleNames(nameZhs.get(0)+"|"+nameZhs.get(1));
                }
            } else{//如果展示记录数
                Set<SysRole> rolesSet=user.getRoles();//获取该账户拥有的角色集合
                if(rolesSet!=null&&rolesSet.size()>0){//如果用户的角色集合存在，说明不是手动创建的对象，即非示例行
                    SysRole[] rolesArray=rolesSet.toArray(new SysRole[rolesSet.size()]);//转换为数组
                    StringBuilder roleNameString=new StringBuilder();
                    //将角色集合转换为字符串形式在单元格显示，例如：“超级管理员|普通管理员”
                    for(int i=0;i<rolesArray.length;i++){
                        SysRole role=rolesArray[i];
                        roleNameString.append(role.getNameZh());
                        if(i!=rolesArray.length-1){
                            roleNameString.append("|");
                        }
                    }
                    row.setRoleNames(roleNameString.toString());
                }
            }
            if(isTemplate) {//如果是示例
                //从数据库读取所有部门中文名
                List<String> departmentNames=sysDepartmentMapper.getAllNames();
                if(departmentNames.size()>0){//默认选择的部门
                    row.setDepartmentName(departmentNames.get(0));
                }
            }else{
                //设置部门信息
                if(user.getDepartment()!=null){//如果部门存在
                    row.setDepartmentName(user.getDepartment().getName());
                }
            }
        }
        return row;
    }
    
    @Override
    public ExcelWriter exportExcel(boolean isTemplate,SysUserQuery sysUserQuery) {
        //通过工具类创建writer
        ExcelWriter writer = ExcelUtil.getWriter(true);
        //设置表格全局样式
        MyHutoolCustomExcelUtil.setBaseGlobalStyle(writer);
        //实现第一个sheet内容
        this.writeExportUserSheet(writer,isTemplate,sysUserQuery);
        //添加第二个sheet:角色表
        this.writeExportRoleSheet(writer);
        //添加第三个sheet:部门表
        this.writeExportDepartmentSheet(writer);
        return writer;
    }

    /**
     * 输出用户列表sheet，默认是第一个sheet
     * 说明：
     * 1.如果是模板，则输出标题栏加示例；
     * 2.如果不是模板，则根据查询条件输出所有记录
     * @param writer
     * @param isTemplate 是否是模板
     * @param sysUserQuery 查询条件
     */
    private void writeExportUserSheet(ExcelWriter writer,boolean isTemplate,SysUserQuery sysUserQuery){
        // 定义用户记录数集合
        List<SysUserRow> rows=new ArrayList<>();
        //自定义账户对象对应的标题别名,因为是LinkedHashMap，此处的keyset返回的是有序的。
//        Set<Map.Entry<String, String>> entryseSet=SYSUSERBEAN_MAP.entrySet();
//        for(Map.Entry<String, String> entry:entryseSet){
//            //自定义账户对象对应的标题别名
//            writer.addHeaderAlias(entry.getKey(),entry.getValue());
//        }
        //步骤一：重命名当前工作表名称。
        writer.renameSheet(sheetName);
        //步骤二：设置列宽
        //获取set集合，因为是LinkedHashMap集合，因此转换过来的是有序的set集合
        Set<String> keySet=SYSUSERBEAN_MAP.keySet();
        //转为list集合,用于在后面能够根据键值获取所在的列索引（避免直接操作列索引导致的灵活性不足）
        List<String> keyList=new ArrayList<String>(keySet);
        writer.setColumnWidth(keyList.indexOf("username"),20);
        writer.setColumnWidth(keyList.indexOf("nickName"),20);
        //设置账户状态列宽
        writer.setColumnWidth(keyList.indexOf("statusString"),20);
        //设置角色集合列宽
        writer.setColumnWidth(keyList.indexOf("roleNames"),30);
        //设置部门名称列宽
        writer.setColumnWidth(keyList.indexOf("departmentName"),30);
        writer.setColumnWidth(keyList.indexOf("description"),50);
        writer.setColumnWidth(keyList.indexOf("createTime"),20);
        writer.setColumnWidth(keyList.indexOf("updateTime"),20);
        //步骤三：设置标题行的别名Map，以便直接通过Model对象生成表格数据，并且能够在model对象有数据时自动生成标题
        writer.setHeaderAlias(SYSUSERBEAN_MAP);
        //步骤四：添加数据行
        //从数据库读取所有部门中文名
        List<String> departmentNames=sysDepartmentMapper.getAllNames();
        if(isTemplate){//如果是模板，则在数据行添加示例数据
            //定义示例数据
            SysUser user=new SysUser();
            user.setUsername("user1");
            user.setNickName("张三");
            user.setSex(1);
            user.setStatus(1);
            user.setDescription("这是一个账户例子，导入时请删除");
            //定义行记录对象,并将对象转换过来
            SysUserRow row=this.convertSysUserToExcelRow(user,true);
            //将记录添加到集合
            rows.add(row);
        }else{//如果不是模板，则在数据行添加数据行记录
            List<SysUser> users = sysUserMapper.findAll(sysUserQuery);
            for(int i=0;i<users.size();i++){
                SysUser user=users.get(i);
                //定义行记录对象,并将对象转换过来
                SysUserRow row=this.convertSysUserToExcelRow(user,false);
                //将记录添加到集合
                rows.add(row);
            }
        }
        //步骤五：一次性写出内容，使用默认样式，强制输出标题。如果没有额外自定义样式，Hutool工具会自动生成统一的样式。但是如果使用了自定义样式这里就不会变化
        writer.write(rows, true);
        //步骤六：针对特殊单元格进行处理，包括值和特殊样式的处理
        //在数据行范围内为部门列设置部门列表下拉框
        String[] departmentArray=departmentNames.toArray(new String[departmentNames.size()]);
        for(int rowIndex=1;rowIndex<=rows.size();rowIndex++){
            writer.addSelect(6,rowIndex,departmentArray);
        }
        if(isTemplate){//如果是模板，将第一行样式标为红色，作为示例。必须放在数据都添加后设置才有用。
            // 为第一个单元格赋值，因为采用bean来传值的话，第一个单元格为Long类型，无法进行字符串复制。因此这里手动复制
            writer.writeCellValue(0,1,"示例：");
            //将原有单元格样式基础上将字体颜色修改为红色（注意看函数说明，这里会保留原有样式）
            CellStyle cellStyle= MyHutoolCustomExcelUtil.createRedCellStyle(writer, HorizontalAlignment.CENTER);
            //设置第一行包含有数据的样式为红色字体样式
            writer.setRowStyleIfHasData(1,cellStyle);
        }
    }

    /**
     *输出角色sheet
     * @param writer
     */
    private void writeExportRoleSheet(ExcelWriter writer){
        //自定义需要读取或写出的Sheet，如果给定的sheet不存在，创建之。
        writer.setSheet("角色字典表（参考）");
        //将字体恢复为原有样式
        //CellStyle cellStyle= MyHutoolCustomExcelUtil.createNormalCellStyle(writer,HorizontalAlignment.CENTER);
        //必须要清掉上一个表设置的HeaderAlias，否则会混用
        writer.clearHeaderAlias();
        writer.addHeaderAlias("nameZh","中文名");
        writer.addHeaderAlias("level","级别");
        writer.addHeaderAlias("enabled","是否启用");
        writer.addHeaderAlias("defaultRole","是否默认");
        writer.addHeaderAlias("description","描述");
        //设置角色集合
        List<SysRole> roles=sysRoleMapper.findAll(null);//查找所有角色
        // 默认的，未添加alias的属性也会写出，如果想只写出加了别名的字段，可以调用此方法排除之
        writer.setOnlyAlias(true);
        // 合并单元格后的标题行，使用默认标题样式
        writer.merge(writer.getHeaderAlias().size()-1, "系统角色列表");
        // 一次性写出内容，使用默认样式，强制输出标题
        writer.write(roles, true);
    }

    /**
     *输出部门sheet
     * @param writer
     */
    private void writeExportDepartmentSheet(ExcelWriter writer){
        //自定义需要读取或写出的Sheet，如果给定的sheet不存在，创建之。
        writer.setSheet("部门字典表（参考）");
        //将字体恢复为原有样式
        //CellStyle cellStyle= MyHutoolCustomExcelUtil.createNormalCellStyle(writer,HorizontalAlignment.CENTER);
        //必须要清掉上一个表设置的HeaderAlias，否则会混用
        writer.clearHeaderAlias();
        writer.addHeaderAlias("id","部门编号");
        writer.addHeaderAlias("name","部门名称");
        //设置角色集合
        List<SysDepartment> departments=sysDepartmentMapper.findAll(null);//查找所有部门
        // 默认的，未添加alias的属性也会写出，如果想只写出加了别名的字段，可以调用此方法排除之
        writer.setOnlyAlias(true);
        // 合并单元格后的标题行，使用默认标题样式
        writer.merge(writer.getHeaderAlias().size()-1, "部门列表");
        // 一次性写出内容，使用默认样式，强制输出标题
        writer.write(departments, true);
    }

/*    @Override
    public ExcelWriter exportExcel() {
        // 定义用户集合
        List<SysUser> rows=sysUserMapper.findAll(null);
        //List<SysUser> rows=new ArrayList<>();
        //通过工具类创建writer
        ExcelWriter writer = ExcelUtil.getWriter(true);
        //自定义账户对象对应的标题别名
        writer.addHeaderAlias("username","用户名");
        writer.addHeaderAlias("nickName","昵称");
        writer.addHeaderAlias("sex","性别");
        writer.addHeaderAlias("status","账户状态");
        writer.addHeaderAlias("createTime","创建时间");
        // 默认的，未添加alias的属性也会写出，如果想只写出加了别名的字段，可以调用此方法排除之
        writer.setOnlyAlias(true);
        // 一次性写出内容，使用默认样式，强制输出标题
        writer.write(rows, true);
        return writer;
    }*/

    @Override
    public ExcelImportResult addUsersFromExcel(InputStream inputStream, @NotNull(message = "请先登录") SysUser user) {
        ExcelImportResult result=new ExcelImportResult();
        if(sysUserService.isSyperUser(user.getId())){//如果是超级用户
            //读取账户信息
            List<SysUser> users=validateAndReadExcel(inputStream);
            //从账户信息中将需要批量插入和批量更新的记录进行归类
            List<SysUser> newUsers=new ArrayList<>();//新插入的记录，为了保证插入顺序，并且User重写了equal方法（根据id判断相等），必须使用list
            Set<SysUser> oldUsers=new HashSet<>();//需要更新的记录，因为无所谓顺序，所以用set集合

            //为其余属性进行处理
            //对角色进行处理
            //将要变更的账户角色信息进行处理
            List<SysUserRole>  userRolesOfAdd=new ArrayList<>();//定义要批量添加的用户角色关系
            List<SysUserRole>  userRolesOfDelete=new ArrayList<>();//定义要批量移除的用户角色关系
            //获取所有的角色,放在这里是为了提升性能
            List<SysRole> rolesOfAll=sysRoleMapper.findAll(null);
            for(SysUser userOfEach:users){
                if(userOfEach.getId()!=null){//如果需要更新的记录
                    oldUsers.add(userOfEach);
                    result.setUpdateNumber(result.getUpdateNumber()+1);
                    //批量变更角色
                    //将前端传递过来的集合转换为set集合,顺便去重
                    Set<Integer> roleIdsSet = new HashSet<Integer>(Arrays.asList(userOfEach.getRoleIds()));
                    //获取原来的角色
                    List<SysRole> rolesOrigin=sysUserRoleMapper.findAllRoleByUserId(userOfEach.getId());
                    //处理账户和角色的关系
                    for(SysRole role:rolesOfAll){
                        if(roleIdsSet.contains(role.getId())){//如果本次使用了该角色
                            if(!rolesOrigin.contains(role)){//如果该角色原来不存在
                                //添加该角色关系
                                userRolesOfAdd.add(new SysUserRole(userOfEach,new SysRole(role.getId())));
                            }
                        }else{//如果没有勾选该角色
                            if(rolesOrigin.contains(role)) {//如果该角色原来存在
                                //需要移除该的角色关系
                                userRolesOfDelete.add(new SysUserRole(userOfEach,new SysRole(role.getId())));
                            }
                        }
                    }
                }else{
                    //设置初始密码为123456
                    userOfEach.setPassword(SHA.getResult("123456"));
                    newUsers.add(userOfEach);
                    result.setInsertNumber(result.getInsertNumber()+1);
                }
            }
            //批量插入记录
            sysUserMapper.saveAllIn(newUsers);
            //批量编辑记录
            sysUserMapper.updateAllIn(oldUsers);
            //对新角色进行角色处理，放在这里的原因是此时已经完成数据的添加，获取到了新插入记录的主键，才能进行关系赋予。
            for(SysUser userOfEach:newUsers){
                for(Integer roleId:userOfEach.getRoleIds()){//遍历要插入的新角色
                    SysUserRole userRole=new SysUserRole();
                    userRole.setSysUser(userOfEach);
                    userRole.setSysRole(new SysRole(roleId));
                    userRolesOfAdd.add(userRole);
                }
            }
            //批量删除账户角色关系
            sysUserRoleMapper.deleteAllIn(userRolesOfDelete);
            //批量添加账户角色关系
            sysUserRoleMapper.saveAllIn(userRolesOfAdd);
        }else{
            throw new AccessDeniedException("只有超级用户才能导入");
        }
        return result;
    }

    /**
     * 验证表中的字段并返回用户集合
     * 说明：用户创建时间、最后编辑时间不导入
     * @param inputStream
     */
    public List<SysUser> validateAndReadExcel(InputStream inputStream){
        //创建用户对象集合
        List<SysUser> users=new ArrayList<>();
        //定义异常集合，用于返回具体的单元格错误异常信息
        List<ExcelExceptionMessage> excelExceptions=new ArrayList<ExcelExceptionMessage>();
        //获取工作簿对象
        ExcelReader excelReader = ExcelUtil.getReader(inputStream);
        //ExcelReader excelReader = ExcelUtil.getReader(inputStream,"账户列表");
        //获取所有工作表对象
        List<Sheet> sheets=excelReader.getSheets();
        boolean containsSheet=false;//是否包含指定sheet
        for(Sheet sheet:sheets){//遍历所有工作表
            if(sheet.getSheetName().equals(sheetName)){// 如果sheet名为指定的名称
                containsSheet=true;
                //根据该工作表重新创建一个ExcelReader对象
                excelReader=new ExcelReader(sheet);
                //首先判断表中的账户名是否存在重复信息
                if(MyTableUtil.validateRepeatColumn(excelReader.readColumn(1,1, excelReader.getRowCount()))){
                    throw new MyServiceException("导入失败：表格中的账户名存在重复字段");
                }
                //首先判断表中的网名是否存在重复信息。20221129取消昵称重名设定
               /* if(MyTableUtil.validateRepeatColumn(excelReader.readColumn(2,1, excelReader.getRowCount()))){
                    throw new MyServiceException("导入失败：表格中的昵称存在重复字段");
                }*/
                //准备录入数据中要多次查找数据库的数据，避免后面遍历时重复查找，提升性能
                List<SysRole> roles=sysRoleMapper.findAll(null);//获取数据库所有角色
                List<SysDepartment> departments=sysDepartmentMapper.findAll(null);
                //获取set集合，因为是LinkedHashMap集合，因此转换过来的是有序的set集合
                Collection<String> valueSet=SYSUSERBEAN_MAP.values();
                //转为list集合,用于在后面能够根据键值获取所在的列索引（避免直接操作列索引导致的灵活性不足）
                List<String> keyList=new ArrayList<String>(valueSet);
                //读取第二行到最后一行数据
                //List<List<Object>> rows = excelReader.read(1, excelReader.getRowCount());
                List<Map<String,Object>> rows = excelReader.read(0,1, excelReader.getRowCount());
                //遍历每行的数据
                for(int rowIndex=0;rowIndex<rows.size();rowIndex++){
                    SysUser user=new SysUser();
                    Map<String,Object> row=rows.get(rowIndex);//获取当前行的数据
                    //第1列：账户id。如果id为空，表示该账户是要插入的账户；否则则是要更新相关信息
                    String userIdString=String.valueOf(row.get("用户编号"));
                    //如果id存在则赋值
                    if(StringUtils.hasLength(userIdString)){
                        try {
                            user.setId(Long.valueOf(userIdString));
                            //验证id是否在数据库存在，屏蔽无效id。这里用count，提升性能
                            SysUserQuery sysUserQuery=new SysUserQuery();
                            sysUserQuery.setId(user.getId());
                            if(sysUserMapper.count(sysUserQuery)==0){
                                excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("用户编号"),userIdString,"用户主键在系统中不存在"));
                            }
                        } catch (NumberFormatException e) {
                            excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("用户编号"),userIdString,"用户主键必须为整数"));
                        }
                    }
                    //第2列：账户名
                    String username=String.valueOf(row.get("账户名"));
                    //如果账户名存在则赋值
                    if(StringUtils.hasLength(username)){
                        user.setUsername(username);
                        if(user.getId()!=null){//如果不存在说明是要编辑，则要判定是否重名
                            if(sysUserMapper.countByUsernameAndIdNot(username,user.getId())>0){//判断数据库中的账户是否重名
                                excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("账户名"),username,"账户名在系统中重名"));
                            }
                        }else{//如果是要插入账户
                            if(sysUserMapper.countByUsername(username)>0){//判断数据库中的账户是否重名
                                excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("账户名"),username,"账户名在系统中重名"));
                            }
                        }
                    }else{
                        excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("账户名"),username,"账户名不能为空"));
                    }
                    //第3列：昵称
                    String nickName=String.valueOf(row.get("昵称"));
                    //如果姓名存在则赋值
                    if(StringUtils.hasLength(nickName)){
                        user.setNickName(nickName);
                        // 20221129 取消昵称重名设定
                        /*if(user.getId()!=null){//如果不存在说明是要编辑，则要判定是否重名
                            if(sysUserMapper.countByNickNameAndIdNot(nickName,user.getId())>0){//判断数据库中的昵称是否重名
                                excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("昵称"),nickName,"昵称和系统中的重名"));
                            }
                        }else{//如果是要插入账户
                            if(sysUserMapper.countByNickName(nickName)>0){//判断数据库中的昵称是否重名
                                excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("昵称"),nickName,"昵称和系统中的重名"));
                            }
                        }*/
                    }else{
                        excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("昵称"),nickName,"昵称不能为空"));
                    }
                    //第4列：性别
                    String sexString=String.valueOf(row.get("性别"));
                    if(StringUtils.hasLength(sexString)){
                        Collection<String> values = SEX_MAP.values();
                        if(values.contains(sexString)){//如果包含的值一样
                            //使用hutool的工具集合进行map双向查找
                            BiMap<Integer,String> biMap = new BiMap<>(SEX_MAP);
                            user.setSex(biMap.getKey(sexString));
                        }else{
                            String sexValues = values.stream().map(String::valueOf).collect(Collectors.joining(","));
                            excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("性别"),sexString,"性别的值只能是:"+sexValues));
                        }
                    }else{
                        excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("性别"),sexString,"性别不能为空"));
                    }
                    //第5列：账户状态
                    String statusString=String.valueOf(row.get("账户状态"));
                    if(StringUtils.hasLength(statusString)){
                        Collection<String> values = STATUS_MAP.values();
                        if(values.contains(statusString)){//如果包含的值一样
                            //使用hutool的工具集合进行map双向查找
                            BiMap<Integer,String> biMap = new BiMap<>(STATUS_MAP);
                            //根据性别的值获取性别的键
                            user.setStatus(biMap.getKey(statusString));
                        }else{
                            String statusValues = values.stream().map(String::valueOf).collect(Collectors.joining(","));
                            excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("账户状态"),statusString,"账户状态的值只能是:"+statusValues));
                        }
                    }else{
                        excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("账户状态"),statusString,"账户状态不能为空"));
                    }
                    //第6列：角色集合
                    String roleNames=String.valueOf(row.get("角色"));
                    if(StringUtils.hasLength(roleNames)){
                        //将角色进行拆分
                        String[] roleNameArray=roleNames.split("\\|");
                        List<Integer> roleIds=new ArrayList<>();
                        for(String roleName:roleNameArray) {
                            boolean existsRole = false;//默认该角色不存在
                            for (SysRole role : roles) {
                                if (role.getNameZh().equals(roleName)) {
                                    existsRole = true;
                                    roleIds.add(role.getId());
                                    break;
                                }
                            }
                            if (!existsRole) {
                                excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(), rowIndex + 1, keyList.indexOf("角色"), roleNames, roleName + "角色在系统中不存在"));
                            }
                        }
                        user.setRoleIds(roleIds.toArray(new Integer[roleIds.size()]));
                    }else {
                        excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("角色"),roleNames,"账户至少包含一个角色"));
                    }
                    //部门名称
                    String departmentName=String.valueOf(row.get("部门名称"));
                    if(StringUtils.hasLength(departmentName)){
                        boolean exist=false;//默认该部门不存在
                        for(SysDepartment sysDepartment:departments){
                            if(sysDepartment.getName().equals(departmentName)){
                                exist=true;
                                user.setDepartment(sysDepartment);
                                break;
                            }
                        }
                        if(!exist){
                            excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("部门名称"),departmentName,departmentName+"在系统中不存在"));
                        }
                    }else{
                        excelExceptions.add(new ExcelExceptionMessage(sheet.getSheetName(),rowIndex+1,keyList.indexOf("部门名称"),statusString,"必须选择部门"));
                    }
                    //账户描述
                    user.setDescription(String.valueOf(row.get("账户描述")));
                    //将账户添加进集合
                    users.add(user);
                }
            }
        }
        if(!containsSheet){//如果指定表不存在
            throw new MyServiceException("导入失败：表格中必须有名称为"+sheetName+"的sheet");
        }
        if(excelExceptions.size()>0){//如果存在异常
            throw new MyExcelException(excelExceptions);
        }
        return users;
    }

}
